package com.ideaaedi.springcloud.jd.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ideaaedi.springcloud.jd.commonds.constant.BaseConstant;
import com.ideaaedi.springcloud.jd.commonds.entity.BaseCodeMsgEnum;
import com.ideaaedi.springcloud.jd.commonds.entity.PageInfo;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysDictPO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysDictAddReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysDictListReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysDictTreeReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysDictUpdateReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysDictDetailRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysDictListRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysDictTreeRespVO;
import com.ideaaedi.springcloud.jd.commonds.exception.BaseException;
import com.ideaaedi.springcloud.jd.commonspring.mapper.SysDictMapper;
import com.ideaaedi.springcloud.jd.commonspring.mybatisplus.ServiceImplExt;
import com.ideaaedi.springcloud.jd.commonspring.mybatisplus.tenant.TenantProvider;
import com.ideaaedi.springcloud.jd.commonspring.validator.ValidId;
import com.ideaaedi.springcloud.jd.user.service.SysDictService;
import org.springframework.beans.BeanUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统-数据字典 服务实现类
 * </p>
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.1.A
 */
@Service
public class SysDictServiceImpl extends ServiceImplExt<SysDictMapper, SysDictPO> implements SysDictService {
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysDictDetailRespVO add(SysDictAddReqVO req) {
        Integer dictTypeId = req.getDictTypeId();
        String code = req.getCode();
        boolean codeUsed = TenantProvider.execGlobal(() -> getOne(new LambdaQueryWrapper<SysDictPO>()
                .select(SysDictPO::getId)
                .eq(SysDictPO::getDictTypeId, dictTypeId)
                .eq(SysDictPO::getCode, code)
        ) != null);
        if (codeUsed) {
            throw new BaseException(BaseCodeMsgEnum.CODE_BEEN_OCCUPIED, code);
        }
        SysDictPO po = new SysDictPO();
        BeanUtils.copyProperties(req, po);
        po.setId(null);
        save(po);
        Integer generateId = po.getId();
        // 自动生成的id, 不能为系统id保留值
        boolean needUpdateId = generateId <= BaseConstant.SYS_INTERNAL_DATA_ID_EQ;
        Integer id;
        if (needUpdateId) {
            id = (int)(BaseConstant.SYS_INTERNAL_DATA_ID_EQ + 1);
        } else {
            id = generateId;
        }
        update(new SysDictPO(), new LambdaUpdateWrapper<SysDictPO>()
                .set(needUpdateId, SysDictPO::getId, id)
                .set(SysDictPO::getPath, po.getDictTypeId() + "_" + id)
                .eq(SysDictPO::getId, generateId)
        );
        return detail(id);
    }
    
    @Override
    @ValidId
    @Transactional(rollbackFor = Exception.class)
    public SysDictDetailRespVO delete(Integer id) {
        Objects.requireNonNull(id, "id cannot be null.");
        SysDictDetailRespVO resp = detail(id);
        update(new SysDictPO(), new LambdaUpdateWrapper<SysDictPO>()
                .set(SysDictPO::getDeleted, 1)
                .set(SysDictPO::getDeletedTime, System.currentTimeMillis() / 1000)
                .eq(SysDictPO::getId, id)
        );
        return resp;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysDictDetailRespVO update(SysDictUpdateReqVO req) {
        Integer id = req.getId();
        Objects.requireNonNull(id, "id cannot be null.");
        Integer dictTypeId;
        if (req.getDictTypeId() == null) {
            dictTypeId = TenantProvider.execGlobal(() -> getOne(new LambdaQueryWrapper<SysDictPO>()
                    .select(SysDictPO::getId, SysDictPO::getDictTypeId)
                    .eq(SysDictPO::getId, id)
            ).getDictTypeId());
            Objects.requireNonNull(dictTypeId, "dictTypeId should not be null. dict id -> " + id);
        } else {
            dictTypeId = req.getDictTypeId();
        }
        String code = req.getCode();
        if (code != null) {
            boolean codeUsed = TenantProvider.execGlobal(() -> getOne(new LambdaQueryWrapper<SysDictPO>()
                    .select(SysDictPO::getId)
                    .eq(SysDictPO::getDictTypeId, dictTypeId)
                    .eq(SysDictPO::getCode, code)
                    .ne(SysDictPO::getId, id)
            ) != null)  ;
            if (codeUsed) {
                throw new BaseException(BaseCodeMsgEnum.CODE_BEEN_OCCUPIED, code);
            }
        }
        // mybatis-plus默认的updateById方法，更新策略默认是NOT_NULL（即更新数据时数据为NULL值时将不更新进数据库）
        SysDictPO po = new SysDictPO();
        BeanUtils.copyProperties(req, po);
        if (dictTypeId != null) {
            po.setPath(dictTypeId + "_" + id);
        }
        updateById(po);
        return detail(id);
    }
    
    @Override
    public SysDictDetailRespVO detail(Integer id) {
        Objects.requireNonNull(id, "id cannot be null.");
        SysDictPO po = getById(id);
        if (po == null) {
            return null;
        }
        SysDictDetailRespVO resp = new SysDictDetailRespVO();
        BeanUtils.copyProperties(po, resp);
        return resp;
    }
    
    @Override
    public PageInfo<SysDictListRespVO> list(SysDictListReqVO req) {
        int pageNum = req.getPageNum();
        int pageSize = req.getPageSize();
        // 分页查
        IPage<SysDictListRespVO> pageInfo = new Page<>(pageNum, pageSize);
        IPage<SysDictListRespVO> page = this.baseMapper.selectDictForList(pageInfo, req);
        return PageInfo.of(page.getTotal(), pageNum, pageSize, page.getRecords());
    }
    
    @Override
    public Map<String, Map<String, SysDictTreeRespVO>> tree(SysDictTreeReqVO req) {
        Map<String, Map<String, SysDictTreeRespVO>> map = new HashMap<>(256);
        List<SysDictTreeRespVO> records = baseMapper.selectDictForTree(req);
        if (!CollectionUtils.isEmpty(records)) {
            Map<String, List<SysDictTreeRespVO>> typeCodeMap = records.stream()
                    .collect(Collectors.groupingBy(SysDictTreeRespVO::getDictTypeCode));
            typeCodeMap.forEach((typeCode, list) -> {
                Map<String, SysDictTreeRespVO> dictMap = list.stream()
                        .collect(Collectors.toMap(SysDictTreeRespVO::getDictCode, Function.identity()));
                map.put(typeCode, dictMap);
            });
        }
        return map;
    }
    
    @NonNull
    @Override
    public Map<String, String> pathAndNameMap(Collection<String> dictPaths) {
        if (CollectionUtils.isEmpty(dictPaths)) {
            return new HashMap<>(1);
        }
        Set<String> dictPathSet = dictPaths.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(dictPathSet)) {
            return new HashMap<>(1);
        }
        return list(new LambdaQueryWrapper<SysDictPO>()
                .select(SysDictPO::getPath, SysDictPO::getName)
                .in(SysDictPO::getPath, dictPathSet)
        ).stream().collect(Collectors.toMap(SysDictPO::getPath, SysDictPO::getName));
    }
}
